#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/stat.h>
#include <string.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <unistd.h>
#include <time.h>
#define ARGS_CHECK(argc, num)                 \
    {                                         \
        if (argc < num)                       \
        {                                     \
            fprintf(stderr, "args error!\n"); \
            return -1;                        \
        }                                     \
    }
#define ERROR_CHECK(ret, num, msg) \
    {                              \
        if (ret == num)            \
        {                          \
            perror(msg);           \
            return;                \
        }                          \
    }

#define BUFFER_SIZE 4096
int copy_directory(const char *source, const char *destination)
{
    DIR *dir;
    struct dirent *entry;
    struct stat st;

    // 打开源目录
    if (!(dir = opendir(source)))
    {
        perror("opendir");
        return -1;
    }

    // 创建目标目录，如果不存在
    if (stat(destination, &st) != 0)
    {
        if (mkdir(destination, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0)
        {
            perror("mkdir");
            closedir(dir);
            return -1;
        }
    }

    // 遍历源目录
    while ((entry = readdir(dir)) != NULL)
    {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
        {
            continue; // 忽略当前目录和父目录
        }

        char src_path[1024], dst_path[1024];
        snprintf(src_path, sizeof(src_path), "%s/%s", source, entry->d_name);
        snprintf(dst_path, sizeof(dst_path), "%s/%s", destination, entry->d_name);

        // 获取文件状态
        if (stat(src_path, &st) != 0)
        {
            perror("stat");
            closedir(dir);
            return -1;
        }

        // 如果是目录，则递归复制
        if (S_ISDIR(st.st_mode))
        {
            if (copy_directory(src_path, dst_path) != 0)
            {
                closedir(dir);
                return -1;
            }
        }
        // 如果是文件，则直接复制
        else
        {
            FILE *in, *out;
            in = fopen(src_path, "rb");
            if (!in)
            {
                perror("fopen");
                closedir(dir);
                return -1;
            }

            out = fopen(dst_path, "wb");
            if (!out)
            {
                perror("fopen");
                fclose(in);
                closedir(dir);
                return -1;
            }

            char buffer[BUFFER_SIZE];
            size_t n;
            while ((n = fread(buffer, 1, BUFFER_SIZE, in)) > 0)
            {
                if (fwrite(buffer, 1, n, out) != n)
                {
                    perror("fwrite");
                    fclose(in);
                    fclose(out);
                    closedir(dir);
                    return -1;
                }
            }

            if (ferror(in))
            {
                perror("fread");
                fclose(in);
                fclose(out);
                closedir(dir);
                return -1;
            }

            fclose(in);
            fclose(out);
        }
    }

    closedir(dir);
    return 0;
}
char getFileType(__mode_t mode)
{
    if (S_ISDIR(mode))
        return 'd';
    if (S_ISBLK(mode))
        return 'b';
    if (S_ISCHR(mode))
        return 'c';
    if (S_ISFIFO(mode))
        return 'p';
    if (S_ISLNK(mode))
        return 'l';
    if (S_ISSOCK(mode))
        return 's';
    if (S_ISREG(mode))
        return '-';
    return '?';
}

void copy_file(const char *src, const char *dest)
{
    FILE *fp_src = fopen(src, "rb");
    ERROR_CHECK(NULL, fp_src, "Failed to open source file");

    FILE *fp_dest = fopen(dest, "wb");
    ERROR_CHECK(NULL, fp_dest, "Failed to open destination file");

    char buffer[4096];
    size_t bytes_read;
    while ((bytes_read = fread(buffer, 1, sizeof(buffer), fp_src)) > 0)
    {
        fwrite(buffer, 1, bytes_read, fp_dest);
    }

    fclose(fp_dest);
    fclose(fp_src);
}

void create_archive(const char *archive_file, char *files)
{

    mkdir(archive_file, 0775);
    struct stat statBuf;
    int ret = stat(files, &statBuf);
    ERROR_CHECK(-1, ret, "stat");
    char filetype = getFileType(statBuf.st_mode);
    if ('d' == filetype)
    {
        copy_directory(files, archive_file);
    }
    else
    {
        copy_file(files, archive_file);
    }
}

int main(int argc, char *argv[])
{
    ARGS_CHECK(argc, 3);

    if (strcmp(argv[1], "c") == 0) // Create archive
    {
        ARGS_CHECK(argc, 4);
        for (size_t i = 0; i < argc - 3; i++)
        {
            create_archive(argv[2], argv[3]);
        }
    }
    else if (strcmp(argv[1], "x") == 0) // Extract archive
    {
        ARGS_CHECK(argc, 4);
        for (size_t i = 0; i < argc - 3; i++)
        {
            //extract_archive(argv[2], argv[3]);
        }
    }

    else
    {
        fprintf(stderr, "Invalid option. Use 'c' for create, 'x' for extract, or 'copy' for copying files.\n");
        return -1;
    }

    return 0;
}
